package com.zsd.android.complierlib;

import com.google.auto.service.AutoService;
import com.zsd.android.annotationlib.NeedsPermission;
import com.zsd.android.annotationlib.OnNeverAskAgain;
import com.zsd.android.annotationlib.OnPermissionDenied;
import com.zsd.android.annotationlib.OnShowRationale;

import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.JavaFileObject;

@AutoService(Processor.class)
public class PermissionProcessor extends AbstractProcessor {

    //用来报告错误，警告，提示
    private Messager messager;
    //包含很多操作Elements的工具类方法
    private Elements elementsUtils;
    //用来创建新的源文件， class文件(造币技术)
    private Filer filer;
    //包含用于操作TypeMirror工具方法
    private Types typeUtils;

    /**
     * 初始化工作
     * @param processingEnvironment
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        typeUtils = processingEnvironment.getTypeUtils();
        elementsUtils = processingEnvironment.getElementUtils();
        filer = processingEnvironment.getFiler();
        messager = processingEnvironment.getMessager();
    }

    /**
     * 获取支持注解的类型
     * @return
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new LinkedHashSet<>();
        types.add(NeedsPermission.class.getCanonicalName());
        types.add(OnNeverAskAgain.class.getCanonicalName());
        types.add(OnPermissionDenied.class.getCanonicalName());
        types.add(OnShowRationale.class.getCanonicalName());
        return types;
    }

    /**
     * 返回注解支持的最新源版本，JDK
     * 或则可以在类名上面添加
     * @SupportedSourceVersion(SourceVersion.RELEASE_8)
     * 就可以不用重写这个方法
     * @return
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latest();
    }

    /**
     * 注解处理器的核心方法，处理具体的注解实现，生成java文件
     * 强调的是一行一行代码去写
     * @param set
     * @param roundEnvironment
     * @return
     */
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        // 获取Activity中所有带NeedsPermission注解的方法
        Set<? extends Element> needsPermissionSet = roundEnvironment.getElementsAnnotatedWith(NeedsPermission.class);
        //保存起来，键值对：key com.xxx.MainActivity value所有带NeedsPermission注解的方法
        Map<String, List<ExecutableElement>> needsPermissionMap = new HashMap<>();
        // 遍历所有带NeedsPermission的方法
        for (Element element : needsPermissionSet) {
            // 转成方法元素（结构体元素）
            ExecutableElement executableElement = (ExecutableElement) element;
            // 通过方法元素获取它所属的MainActivity类名，如： com.zsd.android.aptdemo.view.activity.MainActivity
            String activityName = getActivityName(executableElement);
            // 从缓存集合中获取MainActivity中所有带NeedsPermission注解的方法集合
            List<ExecutableElement> list = needsPermissionMap.get(activityName);
            if (list == null) {
                list = new ArrayList<>();
                needsPermissionMap.put(activityName, list);
            }
            list.add(executableElement);
        }

        // 获取Activity中所有带OnNeverAskAgain注解的方法
        Set<? extends Element> onNeverAskAgainSet = roundEnvironment.getElementsAnnotatedWith(OnNeverAskAgain.class);
        //保存起来，键值对：key com.xxx.MainActivity value所有带NeedsPermission注解的方法
        Map<String, List<ExecutableElement>> onNeverAskAgainMap = new HashMap<>();
        // 遍历所有带NeedsPermission的方法
        for (Element element : onNeverAskAgainSet) {
            // 转成方法元素（结构体元素）
            ExecutableElement executableElement = (ExecutableElement) element;
            // 通过方法元素获取它所属的MainActivity类名，如： com.zsd.android.aptdemo.view.activity.MainActivity
            String activityName = getActivityName(executableElement);
            // 从缓存集合中获取MainActivity中所有带NeedsPermission注解的方法集合
            List<ExecutableElement> list = onNeverAskAgainMap.get(activityName);
            if (list == null) {
                list = new ArrayList<>();
                onNeverAskAgainMap.put(activityName, list);
            }
            list.add(executableElement);
        }

        // 获取Activity中所有带OnPermissionDenied注解的方法
        Set<? extends Element> onPermissionDeniedSet = roundEnvironment.getElementsAnnotatedWith(OnPermissionDenied.class);
        //保存起来，键值对：key com.xxx.MainActivity value所有带NeedsPermission注解的方法
        Map<String, List<ExecutableElement>> onPermissionDeniedMap = new HashMap<>();
        // 遍历所有带NeedsPermission的方法
        for (Element element : onPermissionDeniedSet) {
            // 转成方法元素（结构体元素）
            ExecutableElement executableElement = (ExecutableElement) element;
            // 通过方法元素获取它所属的MainActivity类名，如： com.zsd.android.aptdemo.view.activity.MainActivity
            String activityName = getActivityName(executableElement);
            // 从缓存集合中获取MainActivity中所有带NeedsPermission注解的方法集合
            List<ExecutableElement> list = onPermissionDeniedMap.get(activityName);
            if (list == null) {
                list = new ArrayList<>();
                onPermissionDeniedMap.put(activityName, list);
            }
            list.add(executableElement);
        }

        // 获取Activity中所有带OnShowRationale注解的方法
        Set<? extends Element> onShowRationaleSet = roundEnvironment.getElementsAnnotatedWith(OnShowRationale.class);
        //保存起来，键值对：key com.xxx.MainActivity value所有带NeedsPermission注解的方法
        Map<String, List<ExecutableElement>> onShowRationaleMap = new HashMap<>();
        // 遍历所有带NeedsPermission的方法
        for (Element element : onShowRationaleSet) {
            // 转成方法元素（结构体元素）
            ExecutableElement executableElement = (ExecutableElement) element;
            // 通过方法元素获取它所属的MainActivity类名，如： com.zsd.android.aptdemo.view.activity.MainActivity
            String activityName = getActivityName(executableElement);
            // 从缓存集合中获取MainActivity中所有带NeedsPermission注解的方法集合
            List<ExecutableElement> list = onShowRationaleMap.get(activityName);
            if (list == null) {
                list = new ArrayList<>();
                onShowRationaleMap.put(activityName, list);
            }
            list.add(executableElement);
        }

        //-------------------- 造币技术 ------------------
        final String CLASS_SUFFIX = "$Permissions";
        for (String activityName : needsPermissionMap.keySet()) {
            List<ExecutableElement> needsPermissionElements = needsPermissionMap.get(activityName);
            List<ExecutableElement> onNeverAskAgainElements = onNeverAskAgainMap.get(activityName);
            List<ExecutableElement> onPermissionDeniedElements = onPermissionDeniedMap.get(activityName);
            List<ExecutableElement> onShowRationale = onShowRationaleMap.get(activityName);
            Filer filer = processingEnv.getFiler();
            Writer writer = null;
            //创建一个新的源文件（Class），并返回一个对象以允许写入它
            try {
                // 创建一个新的源文件（Class），并返回一个对象以允许写入它（MainActivity$Permissions）
                JavaFileObject javaFileObject = filer.createSourceFile(activityName + CLASS_SUFFIX);
                // 通过方法标签获取包名标签（任意一个标签的父节点都是同一个包名）
                String packageName = getPackageName(needsPermissionElements.get(0));

                writer = javaFileObject.openWriter();
                //类名：MainActivity$Permissions，不是com.zsd.android.aptdemo.MainActivity$Permission
                // 通过属性元素获取它所属的MainActivity类名，再拼接后结果为：MainActivity$Permissions
                String activitySimpleName = needsPermissionElements.get(0).getEnclosingElement()
                        .getSimpleName().toString() + CLASS_SUFFIX;

                //生成包
                writer.write("package " + packageName + ";\n");
                //生成要导入的接口
                writer.write("import com.zsd.android.library.listener.RequestPermission;\n");
                writer.write("import com.zsd.android.library.listener.PermissionRequest;\n");
                writer.write("import com.zsd.android.library.utils.PermissionHelper;\n");
                writer.write("import android.support.v7.app.AppCompatActivity;\n");
                writer.write("import android.support.v4.app.ActivityCompat;\n");
//                writer.write("import android.support.v7.app.AppCompat;\n");
                writer.write("import android.support.annotation.NonNull;\n");
                writer.write("import java.lang.ref.WeakReference;\n");
                //生成类
                writer.write("public class " + activitySimpleName + " implements RequestPermission<" + activityName + "> {\n");
                writer.write("@Override\n");
                writer.write("public void requestPermission (" + activityName + " target, String[] permissions) {\n}\n");

                writer.write("@Override\n");
                writer.write("public void onRequestPermissionResult (" + activityName + " target, int requestCode, int[] permissions) {\n}\n");

                writer.write("\n}");
                writer.flush();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

    private String getActivityName(ExecutableElement executableElement) {
        // 通过方法标签获取类名标签，
        TypeElement typeElement = (TypeElement) executableElement.getEnclosingElement();
        return typeElement.getSimpleName().toString();
    }

    private String getPackageName(ExecutableElement executableElement) {
        // 通过方法标签获取类名标签
        TypeElement typeElement = (TypeElement) executableElement.getEnclosingElement();
        // 通过类名标签获取包名标签，
        String packageName = processingEnv.getElementUtils().getPackageOf(typeElement).getQualifiedName().toString();
        return packageName;
    }

    private String getPackageName(TypeElement typeElement) {
        PackageElement packageElement = (PackageElement) typeElement.getEnclosingElement();
        return packageElement.getQualifiedName().toString();
    }
}
